/*
 *  Copyright (c) 2001 Sun Microsystems, Inc.  All rights
 *  reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following discalimer in
 *  the documentation and/or other materials provided with the
 *  distribution.
 *
 *  3. The end-user documentation included with the redistribution,
 *  if any, must include the following acknowledgment:
 *  "This product includes software developed by the
 *  Sun Microsystems, Inc. for Project JXTA."
 *  Alternately, this acknowledgment may appear in the software itself,
 *  if and wherever such third-party acknowledgments normally appear.
 *
 *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
 *  must not be used to endorse or promote products derived from this
 *  software without prior written permission. For written
 *  permission, please contact Project JXTA at http://www.jxta.org.
 *
 *  5. Products derived from this software may not be called "JXTA",
 *  nor may "JXTA" appear in their name, without prior written
 *  permission of Sun.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 *  ====================================================================
 *
 *  This software consists of voluntary contributions made by many
 *  individuals on behalf of Project JXTA.  For more
 *  information on Project JXTA, please see
 *  <http://www.jxta.org/>.
 *
 *  This license is based on the BSD license adopted by the Apache Foundation.
 *
 *  $Id: DialogMessage.java,v 1.5 2007/05/23 21:00:18 nano Exp $
 */

package net.jxta.myjxta.dialog;

import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.myjxta.dialog.util.Text;

import java.util.*;

/**
 * @author james todd [gonzo at jxta dot org]
 * @version $Id: DialogMessage.java,v 1.5 2007/05/23 21:00:18 nano Exp $
 */

public class DialogMessage {

    /**
     * The default value for the originator of the DialogMessage
     */
    public static final String DEFAULT_ORIGINATOR = "anonymous";

    /**
     * The default PeerGroup id of the originator of the DialogMessage
     */
    public static final String DEFAULT_GROUP_ID = "unknown";

    /**
     * The default group of the originator of this DialogMessage
     */
    public static final String DEFAULT_GROUP = "unknown";

    /**
     * The character to use as a new line character
     */
    private static final char NEW_LINE = '\n';

    private static final DialogKeys KEYS = new DialogKeys();

    private DialogKeys keys = KEYS;

    /**
     * The originator of this DialogMessage
     */
    private String originator = null;

    /**
     * The Message encapsulated by this DialogMessage object
     */
    private String htmlMessage = null;

    private String plainTextMessage = null;

    public String getPlainTextMessage() {
        return plainTextMessage;
    }

    public void setPlainTextMessage(String p_plainTextMessage) {
        plainTextMessage = p_plainTextMessage;
    }

    /**
     * The PeerGroup id of the originator of the DialogMessage
     */
    private String groupId = null;

    /**
     * The group of the originator of this DialogMessage
     */
    private String groupName = null;

    /**
     * The command encapsulated by this DialogMessage
     */
    private String command = null;

    /**
     * The label of this DialogMesage instance
     */
    private String label = null;

    /**
     * The time this DialogMessage was created
     */
    private Date timeStamp = null;

    private Map<String, MessageElement> elements = null;


    /**
     * Create a new DialogMessage object using all the parameters
     * set in dm.
     *
     * @param dm the DialogMessage object to clone
     */
    public DialogMessage(DialogMessage dm) {
        this(dm.getOriginator(), dm.getHtmlMessage(), dm.getGroupId(),
                dm.getGroupName());

        setCommand(dm.getCommand());
        setLabel(dm.getLabel());
        setTimeStamp(dm.getTimeStamp());
        setPlainTextMessage(dm.getPlainTextMessage());

        setKeys(dm.getKeys());

        Map<String, MessageElement> messageElements = dm.getMessageElements();
        addMessageElements(messageElements);
    }

    /**
     * Create a new DialogMessage object that encapsulates the
     * indicated htmlMessage
     *
     * @param message the htmlMessage of the new DialogMessage object
     */
    public DialogMessage(String message) {
        this(null, message, null, null);
    }

    /**
     * Create a new DialogMessage object with the indicated originator
     * and htmlMessage
     *
     * @param originator  the originator of this DialogMessage object
     * @param htmlMessage the htmlMessage of the new DialogMessage object
     */
    public DialogMessage(String originator, String htmlMessage,
                         String groupId, String groupName) {
        this(originator, htmlMessage, groupId, groupName, KEYS);
    }

    public DialogMessage(String originator, String message, String groupId,
                         String groupName, DialogKeys keys) {
        this.originator = originator != null ?
                originator : DEFAULT_ORIGINATOR;
        this.htmlMessage = message != null ? message.trim() : "";
        this.groupId = groupId != null ? groupId : DEFAULT_GROUP_ID;
        this.groupName = groupName != null ? groupName : DEFAULT_GROUP;

        this.timeStamp = new Date();
        this.keys = keys;
    }

    public DialogMessage(Message msg) {
        this(msg, KEYS);
    }

    public DialogMessage(Message msg, DialogKeys keys) {
        this(getElement(msg, keys.originator),
                getElement(msg, keys.message, getElement(msg, keys.legacyMessage)),
                getElement(msg, keys.groupId), getElement(msg, keys.groupName),
                keys);

        setCommand(getElement(msg, keys.command));
        setPlainTextMessage(getElement(msg, keys.legacyMessage));
        // xxx: hackary
        MessageElement me;
        String n;

        for (Message.ElementIterator mi = msg.getMessageElements(); mi.hasNext();) {
            me = mi.next();
            n = me.getElementName();

            if (!keys.originator.equals(n) &&
                    !keys.message.equals(n) &&
                    !keys.message.equals(n) &&
                    !keys.legacyMessage.equals(n) &&
                    !keys.groupId.equals(n) &&
                    !keys.groupName.equals(n) &&
                    !keys.command.equals(n)) {
                addMessageElement(n, me);
            }
        }
    }

    public DialogKeys getKeys() {
        return this.keys;
    }

    /**
     * Get the originator of this DialogMesage object
     *
     * @return the originator of this DialogMesage object
     */
    public String getOriginator() {
        return this.originator;
    }

    /**
     * Changes the originator of this DialogMessage object
     *
     * @param originator the originator of this DialogMessage object
     */
    public void setOriginator(String originator) {
        this.originator = originator != null ?
                originator : DEFAULT_ORIGINATOR;
    }

    /**
     * Get the htmlMessage encapsulated by this DialogMessage object
     *
     * @return the htmlMessage encapsulated by this DialogMessage object
     */
    public String getHtmlMessage() {
        return this.htmlMessage;
    }

    /**
     * Changes the htmlMessage encapsulated by this DialogMessage object
     *
     * @param msg the new htmlMessage
     */
    public void setHtmlMessage(String msg) {
        this.htmlMessage = msg != null ? msg.trim() : "";
    }

    /**
     * Get the PeerGroup Id of the originator of this DialogMessage object
     *
     * @return the PeerGroup Id of the originator of this DialogMessage object
     */
    public String getGroupId() {
        return this.groupId;
    }

    /**
     * Changes the PeerGroup Id of the originator of this DialogMessage object
     *
     * @param groupId the new  PeerGroup Id
     */
    public void setGroupId(String groupId) {
        this.groupId = groupId != null ? groupId : DEFAULT_GROUP_ID;
    }

    /**
     * Get the PeerGroup name of the originator of this DialogMessage object
     *
     * @return the PeerGroup name
     */
    public String getGroupName() {
        return this.groupName;
    }

    /**
     * Changes the PeerGroup name of the originator of this DialogMessage object
     *
     * @param groupName the new PeerGroup name
     */
    public void setGroupName(String groupName) {
        this.groupName = groupName != null ? groupName : DEFAULT_GROUP;
    }

    /**
     * Get the command encapsulated by this DialogMessage object
     *
     * @return the command encapsulated by this DialogMessage object
     */
    public String getCommand() {
        return this.command;
    }

    /**
     * Changes the  command encapsulated by this DialogMessage object
     *
     * @param command the new command encapsulated by this DialogMessage object
     */
    public void setCommand(String command) {
        this.command = command;
    }

    /**
     * Get the label encapsulated by this DialogMessage object
     *
     * @return the label encapsulated by this DialogMessage object
     */
    public String getLabel() {
        return this.label;
    }

    /**
     * Changes the label encapsulated by this DialogMessage object
     *
     * @param label the new label encapsulated by this DialogMessage object
     */
    public void setLabel(String label) {
        this.label = label;
    }

    /**
     * Get the time at which this DialogMessage object was created
     *
     * @return the timestamp of this DialogMessage object
     */
    public Date getTimeStamp() {
        return this.timeStamp;
    }

    public MessageElement addMessageElement(String key, MessageElement me) {
        MessageElement ome = null;

        if (key != null && me != null) {
            if (this.elements == null) {
                this.elements = new HashMap<String, MessageElement>();
            }
            ome = this.elements.put(key, me);
        }

        return ome;
    }

    public void addMessageElements(Map<String, MessageElement> me) {
        String key;

        if (me == null) {
            return;
        }

        for (String s : me.keySet()) {
            key = s;

            addMessageElement(key, me.get(key));
        }
    }

    public MessageElement getMessageElement(String key) {
        return this.elements != null ?
                this.elements.get(key) : null;
    }

    public Map<String, MessageElement> getMessageElements() {
        return this.elements;
    }

    public Iterator getMessageElementIterator() {
        return this.elements != null ?
                this.elements.keySet().iterator() :
                Collections.EMPTY_MAP.keySet().iterator();
    }

    public Message toMessage(DialogMessage template) {
        Message msg = new Message();

        overlay(template);
        setLabel(null);

        /// add the relevant messge elements
        String s = getHtmlMessage();

        if (s != null) {
            msg.addMessageElement(new StringMessageElement(keys.message,
                    s.trim(), null));
            if (plainTextMessage == null) {
                String plainText = Text.replace(s.trim(), Text.MARKUP, Text.EMPTY).trim();
                msg.addMessageElement(new StringMessageElement(keys.legacyMessage,
                        plainText,
                        null));
            } else {
                msg.addMessageElement(new StringMessageElement(keys.legacyMessage, plainTextMessage, null));
            }
        }

        s = getOriginator();

        if (s != null) {
            msg.addMessageElement(new StringMessageElement(keys.originator,
                    s, null));
        }

        s = getGroupId();

        if (s != null) {
            msg.addMessageElement(new StringMessageElement(keys.groupId,
                    s, null));
        }

        s = getGroupName();

        if (s != null) {
            msg.addMessageElement(new StringMessageElement(keys.groupName,
                    s, null));
        }

        s = getCommand();

        if (s != null) {
            msg.addMessageElement(new StringMessageElement(keys.command,
                    s, null));
        }

        // xxx: msg.addMessageElement
        String key;

        for (Iterator mei = getMessageElementIterator();
             mei.hasNext();) {
            key = (String) mei.next();

            msg.addMessageElement(getMessageElement(key));
        }

        return msg;
    }

    /**
     * Get a string representation of this DialogMessage object
     *
     * @return a string representation of this DialogMessage object
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("originator: ").append(this.originator).append(NEW_LINE);
        sb.append("htmlMessage: ").append(this.htmlMessage).append(NEW_LINE);
        sb.append("groupId: ").append(this.groupId).append(NEW_LINE);
        sb.append("groupName: ").append(this.groupName).append(NEW_LINE);
        sb.append("command: ").append(this.command).append(NEW_LINE);
        sb.append("label: ").append(this.label).append(NEW_LINE);
        sb.append("timeStamp: ").append(this.timeStamp).append(NEW_LINE);
        sb.append("keys: ").append(this.keys.originator).append(NEW_LINE);

        String k;

        for (Iterator ei = getMessageElementIterator(); ei.hasNext();) {
            k = (String) ei.next();

            sb.append("element: ").append(k).append(NEW_LINE);
            sb.append(getMessageElement(k).toString()).append(NEW_LINE);
        }

        return sb.toString();
    }

    /**
     * Create a copy of this DialogMessage object
     *
     * @return the newly created copy of this DialogMessage object
     */
    @SuppressWarnings({"CloneDoesntCallSuperClone"})
    public Object clone() {
        return new DialogMessage(this);
    }

    /**
     * Get the content of the element with the indicated tag int the
     * Message object msg.
     *
     * @param msg the Message  containing the desired tag
     * @param tag the desired tag
     */
    protected static String getElement(Message msg, String tag) {
        return getElement(msg, tag, null);
    }

    /**
     * Get the content of the element with the indicated tag int the
     * Message object msg.
     *
     * @param msg          the Message  containing the desired tag
     * @param tag          the desired tag
     * @param defaultValue the value to be returned if the desired
     *                     tag was not found
     */
    protected static String getElement(Message msg, String tag,
                                       String defaultValue) {
        MessageElement me = msg.getMessageElement(tag);

        return me != null ? me.toString() : defaultValue;
    }

    protected void setKeys(DialogKeys keys) {
        this.keys = keys;
    }

    /**
     * Make sure that all the relevant tags  of the DialogMessage
     * object are set to sensible default values
     *
     * @param dialogMessage the DialogMessage which to check for sensible default
     *                      values
     */
    protected void overlay(DialogMessage dialogMessage) {
        String s = getOriginator();

        // check that orignator is set
        if (s == null ||
                s.trim().length() == 0 ||
                s.equals(DEFAULT_ORIGINATOR)) {
            setOriginator(dialogMessage.getOriginator());
        }

        // check that there is a useful htmlMessage
        s = getHtmlMessage();

        if (s == null ||
                s.trim().length() == 0) {
            setHtmlMessage(dialogMessage.getHtmlMessage());
        }

        // check that the PeerGroup id is set
        s = getGroupId();

        if (s == null ||
                s.trim().length() == 0 ||
                s.equals(DEFAULT_GROUP_ID)) {
            setGroupId(dialogMessage.getGroupId());
        }

        // check that the group name is set
        s = getGroupName();

        if (s == null ||
                s.trim().length() == 0 ||
                s.equals(DEFAULT_GROUP)) {
            setGroupName(dialogMessage.getGroupName());
        }

        // check that the command is set
        s = getCommand();

        if (s == null ||
                s.trim().length() == 0) {
            setCommand(dialogMessage.getCommand());
        }

        // check that the label is set
        s = getLabel();

        if (s == null ||
                s.trim().length() == 0) {
            setLabel(dialogMessage.getLabel());
        }
    }

    /**
     * Changes the time this DialogMessage was created
     *
     * @param timeStamp the new timestamp  of this DialogMessage object
     */
    private void setTimeStamp(Date timeStamp) {
        this.timeStamp = timeStamp;
    }
}
